package org.opentcs.kernel.extensions.servicewebapi.jjwc.rxx;


import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import lombok.Data;
import lombok.Getter;
import lombok.ToString;
import org.opentcs.access.to.model.*;
import org.opentcs.data.model.*;
import org.opentcs.data.model.visualization.Layer;
import org.opentcs.data.model.visualization.LayerGroup;
import org.opentcs.kernel.extensions.servicewebapi.jjwc.FileWriter;
import org.opentcs.util.fsy.JsonBinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * convert the map of rxx to map of opentcs
 */
@Getter
public class RXXMapConversion {
  private static final Logger LOG = LoggerFactory.getLogger(RXXMapConversion.class);
  private final String mapRequestPath = "/rbrainrobot/request_node_info";

  private final String savePath = System.getProperty("user.dir").replaceAll("openTCS-Kernel", "openTCS-ModelEditor")
                                  + FileSystems.getDefault().getSeparator() + "data" + FileSystems.getDefault().getSeparator();
  private final String failedMag = "failed";

  public boolean convert(String mapData, String ip) {
    //解析地图
    JsonBinder jsonBinder = new JsonBinder();
    rxxMapBase rxxMapBase = jsonBinder.fromJson(mapData, rxxMapBase.class);
    //进行地图转换和保存
    MapData data = rxxMapBase.getData();
    if (failedMag.equalsIgnoreCase(data.getMsg())) {
      LOG.warn("convert rxx map failed mag: {}", data.getError_info());
      return false;
    }
    List<MapInfo> mapInfo = data.getMap_info();
    return convert2KernelMapAndSave(mapInfo, ip);
  }

  private boolean convert2KernelMapAndSave(List<MapInfo> mapInfo, String ip) {
    FileWriter fileWriter = new FileWriter();
    for (MapInfo info : mapInfo) {
      if (!createPlantModelCreationTO(info, ip, fileWriter)) {
        return false;
      }
    }
    return true;
  }

  private boolean createPlantModelCreationTO(MapInfo mapInfo, String ip, FileWriter fileWriter) {
    String mapcode = mapInfo.getMapcode();
    if (mapcode == null) {
      LOG.warn("the mapcode of xrr_car is null,create default map");
      mapcode = "default_map_" + System.currentTimeMillis();
    }
    PlantModelCreationTO plantModel = new PlantModelCreationTO(mapcode);
    VisualLayoutCreationTO defaultVisualLayoutCreationTO = getDefaultVisualLayoutCreationTO();
    plantModel = plantModel.withVisualLayout(defaultVisualLayoutCreationTO)
                     .withPaths(getPathFromRxxMap(mapInfo))
                     .withPoints(getPointFromRxxMao(mapInfo)).withVehicle(createDefaultVehicle(ip));
    try {
      File file = new File(savePath + mapcode + ".xml");
      fileWriter.writeFile(plantModel, file);
    }
    catch (IOException ignored) {
      ignored.printStackTrace();
      LOG.warn("map write failure for xrr conversion");
      return false;
    }
    return true;
  }

  private VehicleCreationTO createDefaultVehicle(String ip) {
    VehicleCreationTO vehicleCreationTO = new VehicleCreationTO("Vehicle-0001");
    return vehicleCreationTO.withProperty("jjwcRxx:vehicleIp", ip)
               .withLength(100)
               .withLayout(new VehicleCreationTO.Layout());
  }

  /**
   * 转化rXX的地图会将地图站点的 x y扩大1000倍
   *
   * @param mapInfo
   * @return
   */
  private List<PointCreationTO> getPointFromRxxMao(MapInfo mapInfo) {
    List<NodeData> node = mapInfo.getNode();
    ArrayList<PointCreationTO> pointCreationTOS = new ArrayList<>();
    for (NodeData nodeData : node) {
      PointCreationTO pointCreationTO = new PointCreationTO(nodeData.getName());
      long x = (long) (nodeData.getX() * 10000);
      long y = (long) (nodeData.getY() * 10000);
      Pose pose = new Pose(new Triple(x, y, 0), nodeData.getAngle());
      PointCreationTO.Layout layout = new PointCreationTO.Layout(new Couple(x, y), new Couple(-10, -20), 0);
      pointCreationTO = pointCreationTO.withLayout(layout).withPose(pose).withProperty("relocate_pos", nodeData.getRelocate_pos().toString());
      pointCreationTOS.add(pointCreationTO);
    }
    return pointCreationTOS;
  }

  private List<PathCreationTO> getPathFromRxxMap(MapInfo mapInfo) {
    ArrayList<PathCreationTO> pathCreationTOS = new ArrayList<>();
    List<PathData> path = mapInfo.getPath();
    for (PathData pathData : path) {
      String pathName = pathData.getPath_name();
      String startName = pathData.getStart_name();
      String goalName = pathData.getGoal_name();
      PathCreationTO pathCreationTO = new PathCreationTO(pathName, startName, goalName);
      PathCreationTO.Layout layout = new PathCreationTO.Layout();
      Integer direction = pathData.getDirection();
      long pathLength = 1000;
      boolean isReverse = false;
      switch (direction) {
        case 0: {
          pathCreationTO.withLayout(layout);
          break;
        }
        case 1: {
          PathCreationTO pathCreationTO2 = new PathCreationTO(goalName + "-" + startName, goalName, startName);
          PathCreationTO.Layout layout2 = new PathCreationTO.Layout();
          pathCreationTO2 = pathCreationTO2.withMaxVelocity(1000).withLength(pathLength).withLayout(layout2)
                                .withProperty("relayPathName", pathName);
          pathCreationTOS.add(pathCreationTO2);
          break;
        }
        case 2: {
          PathCreationTO pathCreationTO2 = new PathCreationTO(goalName + "-" + startName, goalName, startName);
          PathCreationTO.Layout layout2 = new PathCreationTO.Layout();
          pathCreationTO2 = pathCreationTO2.withMaxVelocity(1000).withLength(pathLength).withLayout(layout2)
                                .withProperty("relayPathName", pathName).withProperty("reverse", "true");
          isReverse = true;
          pathCreationTOS.add(pathCreationTO2);
          break;
        }
        case 3: {
          layout.withConnectionType(Path.Layout.ConnectionType.BEZIER);
          layout.withControlPoints(Arrays.asList(new Couple((long) (pathData.getControl_1_x() * 1000), (long) (pathData.getControl_1_y() * 1000)),
                                                 new Couple((long) (pathData.getControl_2_x() * 1000), (long) (pathData.getControl_2_y() * 1000))));
          pathCreationTO.withLayout(layout);
          break;
        }
        case 4: {
          layout.withConnectionType(Path.Layout.ConnectionType.BEZIER);
          layout.withControlPoints(Arrays.asList(new Couple((long) (pathData.getControl_1_x() * 1000), (long) (pathData.getControl_1_y() * 1000)),
                                                 new Couple((long) (pathData.getControl_2_x() * 1000), (long) (pathData.getControl_2_y() * 1000))));
          pathCreationTO.withLayout(layout);

          PathCreationTO pathCreationTO2 = new PathCreationTO(goalName + "-" + startName, goalName, startName);
          PathCreationTO.Layout layout2 = new PathCreationTO.Layout().withConnectionType(Path.Layout.ConnectionType.BEZIER);
          layout2.withControlPoints(Arrays.asList(new Couple((long) (pathData.getControl_1_x() * 1000), (long) (pathData.getControl_1_y() * 1000)),
                                                  new Couple((long) (pathData.getControl_2_x() * 1000), (long) (pathData.getControl_2_y() * 1000))));
          pathCreationTO2 = pathCreationTO2.withMaxVelocity(1000).withLength(pathLength).withLayout(layout2)
                                .withProperty("relayPathName", pathName);
          pathCreationTOS.add(pathCreationTO2);
          break;
        }
        case 5: {
          layout.withConnectionType(Path.Layout.ConnectionType.BEZIER);
          layout.withControlPoints(Arrays.asList(new Couple((long) (pathData.getControl_1_x() * 1000), (long) (pathData.getControl_1_y() * 1000)),
                                                 new Couple((long) (pathData.getControl_2_x() * 1000), (long) (pathData.getControl_2_y() * 1000))));
          pathCreationTO.withLayout(layout);

          PathCreationTO pathCreationTO2 = new PathCreationTO(goalName + "-" + startName, goalName, startName);
          PathCreationTO.Layout layout2 = new PathCreationTO.Layout().withConnectionType(Path.Layout.ConnectionType.BEZIER);
          layout2.withControlPoints(Arrays.asList(new Couple((long) (pathData.getControl_1_x() * 1000), (long) (pathData.getControl_1_y() * 1000)),
                                                  new Couple((long) (pathData.getControl_2_x() * 1000), (long) (pathData.getControl_2_y() * 1000))));
          pathCreationTO2 = pathCreationTO2.withMaxVelocity(1000).withLength(pathLength).withLayout(layout2)
                                .withProperty("relayPathName", pathName).withProperty("reverse", "true");
          isReverse = true;
          pathCreationTOS.add(pathCreationTO2);
          break;
        }
      }
      PathCreationTO pathCreationTO1 = pathCreationTO.withLength(pathLength).withLayout(layout).withMaxVelocity(1000);
      if (isReverse) {
        pathCreationTO1 = pathCreationTO1.withProperty("reverse", "true");
      }
      pathCreationTOS.add(pathCreationTO1);
    }
    return pathCreationTOS;
  }

  /**
   * create a default visualLayout
   *
   * @return
   */
  private VisualLayoutCreationTO getDefaultVisualLayoutCreationTO() {
    VisualLayoutCreationTO visualLayoutCreationTO = new VisualLayoutCreationTO("VLayout");
    Layer layer = new Layer(0, 0, true, "default layer", 0);
    LayerGroup defaultLayerGroup = new LayerGroup(0, "Default layer group", true);
    visualLayoutCreationTO.withLayerGroup(defaultLayerGroup);
    visualLayoutCreationTO.withLayer(layer);
    return visualLayoutCreationTO.withProperty("rxx:carMapConvertKernelMap", new SimpleDateFormat("yy-MM-dd:hh:mm:ss").format(new Date()));
  }
}

@Data
@ToString
class rxxMapBase {
  private MapData data;

}
